﻿using System.Linq.Expressions;

/// <summary>
/// Linq动态拼接
/// </summary>
public static class PredicateBuilder
{
    /// <summary>
    /// True表达式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static Expression<Func<T, bool>> True<T>()
    {
        return f => true;
    }

    /// <summary>
    /// False表达式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static Expression<Func<T, bool>> False<T>()
    {
        return f => false;
    }

    /// <summary>
    /// 表达式拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <param name="merge"></param>
    /// <returns></returns>
    public static Expression<T> Compose<T>(
        this Expression<T> first,
        Expression<T> second,
        Func<Expression, Expression, Expression> merge
    )
    {
        // 构建参数映射（从第二个参数到第一个参数）
        var map = first
            .Parameters.Select((f, i) => new { f, s = second.Parameters[i] })
            .ToDictionary(p => p.s, p => p.f);

        // 将第二个lambda表达式中的参数替换为第一个lambda中的参数
        var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

        // 将lambda表达式体的组合应用于第一个表达式中的参数
        return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
    }

    /// <summary>
    /// 表达式And拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> And<T>(
        this Expression<Func<T, bool>> first,
        Expression<Func<T, bool>> second
    )
    {
        return first.Compose(second, Expression.And);
    }

    /// <summary>
    /// 表达式AndAlso拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> AndAlso<T>(
        this Expression<Func<T, bool>> first,
        Expression<Func<T, bool>> second
    )
    {
        return first.Compose(second, Expression.AndAlso);
    }

    /// <summary>
    /// 表达式Or拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> Or<T>(
        this Expression<Func<T, bool>> first,
        Expression<Func<T, bool>> second
    )
    {
        return first.Compose(second, Expression.Or);
    }

    /// <summary>
    /// 表达式OrElse拼接
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="first"></param>
    /// <param name="second"></param>
    /// <returns></returns>
    public static Expression<Func<T, bool>> OrElse<T>(
        this Expression<Func<T, bool>> first,
        Expression<Func<T, bool>> second
    )
    {
        return first.Compose(second, Expression.OrElse);
    }
}
